package com.xw.service;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.Locale;

import com.baidu.location.BDLocation;
import com.baidu.location.BDLocationListener;
import com.baidu.location.LocationClient;
import com.baidu.location.LocationClientOption;
import com.baidu.mapapi.BMapManager;
import com.baidu.mapapi.map.LocationData;
import com.baidu.mapapi.map.MKMapViewListener;
import com.baidu.mapapi.map.MapController;
import com.baidu.mapapi.map.MapView;
import com.baidu.mapapi.map.MyLocationOverlay;
import com.baidu.platform.comapi.basestruct.GeoPoint;

import com.xw.convert.CRC16;
import com.xw.convert.dataConvert;
import com.xw.socket.Jsocket;

import com.xw.wuliu.DemoApplication;
import com.xw.wuliu.FeedBackForm;
import com.xw.wuliu.MainActivity;
import com.xw.wuliu.MapSignInActivity;
import com.xw.wuliu.MapViewListener;
import com.xw.wuliu.MyInfoActivity;
import com.xw.wuliu.R;
import com.xw.wuliu.MapSignInActivity.MyLocationListenner;

import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.ConditionVariable;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ImageButton;
import android.widget.ListView;
import android.widget.Toast;
import android.widget.AdapterView.OnItemClickListener;

/***
 * 
 * @author gxw
 * 
 *         主要功能： 上传位置信息，下载服务器信息（如公告消息等） 前台SERVICE，提升内存占用优先级
 */

public class LocationTrackerSvc extends Service {

	public LocationClient mLocClient; // 定位客户端

	public LocationListenner myListener = new LocationListenner();// 定位监听，获取位置时会回调成员函数。

	LocationData locData = null;

	private String city = "";
	private String streetName = "";
	private String doorNo = ""; // 门牌号

	private Handler mHandler = null;

	int lat = 0; // 纬度
	int lng = 0; // 经度

	DemoApplication app = null;

	private NotificationManager mNM;

	// 新版设置前台，停止前台服务API函数
	private Method mStartForeground;
	private Method mStopForeground;

	// 旧版设置前台服务的API函数
	private Method mSetForeground;

	// mStartForeground的2个参数，2个参数均为类对象参数，第一个为整型类，第二个为通知类
	private static final Class<?>[] mStartForegroundSignature = new Class[] {
			int.class, Notification.class };

	// 参数为boolean类型
	private static final Class<?>[] mStopForegroundSignature = new Class[] { boolean.class };
	private static final Class<?>[] mSetForegroundSignature = new Class[] { boolean.class };

	// 通上，根据上述参数类型，创建实参
	private Object[] mSetForegroundArgs = new Object[1];
	private Object[] mStartForegroundArgs = new Object[2];
	private Object[] mStopForegroundArgs = new Object[1];

	// variable which controls the notification thread
	private ConditionVariable mCondition;

	/* gxw+s */

	LoginRunnable loginRun;
	Thread loginTrd;

	Jsocket jsock = null;
	boolean boolLogin = false;

	byte[] m_headBytes = new byte[2];
	byte[] m_tailBytes = new byte[2];
	byte[] m_tuidBytes = null;

	private Handler m_handler;

	private final int LEN_BYTES = 2;
	private final int SUM_BYTES = 2;
	private final int CMD_BYTES = 2;
	private int postFailTimes = 0;

	private final int CONNECT_OK = 0;
	private final int CONNECT_FAILED = 1;
	private final int LOGIN_OK = 2;
	private final int LOGIN_FAILED = 3;


	/* gxw+e */

	public void CreateHandler() {
		m_handler = new Handler() {
			public void handleMessage(Message msg) {

				switch (msg.what) {
				case CONNECT_OK:
					Toast.makeText(getApplicationContext(), "TCP连接成功", 1)
							.show();
					break;
					
				case CONNECT_FAILED:
					Toast.makeText(getApplicationContext(), "TCP连接失败", 1)
							.show();
					break;
					
				case LOGIN_OK: // 登录成功后，开始定位
				{

					Toast.makeText(getApplicationContext(), "登录包成功", 1).show();
					if (mLocClient == null || !mLocClient.isStarted())
						InitLocationSvc();
					break;
				}
				case LOGIN_FAILED:

					Toast.makeText(getApplicationContext(), "登录包失败,30秒后自动重新登录", 1).show();
					break;
				}
				
				super.handleMessage(msg);
			}

		};
	}

	/**
	 * 
	 * 初始化位置监听器，并开始定位（GPS和网络混合定位）
	 * 
	 */
	public void InitLocationSvc() {

		locData = new LocationData();
		mLocClient = new LocationClient(this);
		mLocClient.registerLocationListener(myListener);

		// 配置定位选项
		LocationClientOption option = new LocationClientOption();
		option.setOpenGps(true); // 启用gps芯片定位
		// option.setServiceName("com.baidu.location.service_v2.9");
		option.setAddrType("all");
		option.setCoorType("bd09ll "); // bd09bd09ll 设置坐标类型为百度坐标
		option.setScanSpan(30000); // 每隔获取一次最新经纬度

		mLocClient.setLocOption(option);
		mLocClient.start();

	}

	/**
	 * 功能：位置及POI信息监听，获取位置时或者搜索完周边POI信息时，调用回调函数
	 */
	public class LocationListenner implements BDLocationListener {

		// 获得位置时，回调此函数
		@Override
		public void onReceiveLocation(BDLocation location) {
			if (location == null)
				return;

			// 纬度、经度
			locData.latitude = location.getLatitude();
			locData.longitude = location.getLongitude();

			String gpsType = "";
			if (location.getLocType() == BDLocation.TypeNetWorkLocation) { // 网络定位
				// 街道门牌，城市名，省名
				String street = location.getStreet();
				String cityName = location.getCity();
				String province = location.getProvince();
				gpsType = "网络定位";
				city = cityName;
				streetName = street;

			} else if (location.getLocType() == BDLocation.TypeGpsLocation) { // GPS定位
				gpsType = "GPS定位";

			} else {
				gpsType = String.valueOf(location.getLocType());

			}

			// 速度
			locData.speed = location.getSpeed();

			// 方向
			locData.direction = location.getDerect();

			// 精度
			locData.accuracy = location.getRadius();

			// 高度
			double height = 0;
			// height = location.getAltitude();

			// Toast.makeText(getApplicationContext(), "height="+height,
			// Toast.LENGTH_LONG).show();

			Log.i("gps", "height double:" + height);

			// Toast.makeText(getApplicationContext(),
			// "backSvc:" + locData.latitude + "," + locData.longitude,
			// Toast.LENGTH_LONG).show();

			/* gxw+s */
			boolean isPost = PostLocation(locData.latitude, locData.longitude,
					locData.speed, locData.direction, gpsType,
					locData.accuracy, height);

			if (isPost == false) // 如果上传失败，则重新登录 并 重传位置
			{

				boolean isPost2 = PostLocation(locData.latitude,
						locData.longitude, locData.speed, locData.direction,
						gpsType, locData.accuracy, height);

				if (isPost2 == true) {
					postFailTimes = 0;
				} else {
					postFailTimes++;

					if (postFailTimes == 3) {

						// if(loginTrd != null && !loginTrd.isAlive())
						// {
						// loginTrd.start();
						// }
						// else
						// {
						// //在这里重新生成线程
						//
						// }

						postFailTimes = 0;
					}
				}

			}
			/* gxw+e */

		}

		// 获得周边POI信息时，回调
		public void onReceivePoi(BDLocation poiLocation) {
			if (poiLocation == null) {
				return;
			}
		}
	}

	@Override
	public IBinder onBind(Intent intent) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public void onCreate() {

		// 通知栏管理器
		mNM = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);

		Thread notifyingThread = new Thread(null, mTask, "NotifyingService");
		mCondition = new ConditionVariable(false);
		notifyingThread.start();

		// 获得 启动前台 和 停止前台 服务的方法，这是新版本的做法
		try {
			mStartForeground = getClass().getMethod("startForeground",
					mStartForegroundSignature);
			mStopForeground = getClass().getMethod("stopForeground",
					mStopForegroundSignature);

			return; // gxw+

		} catch (NoSuchMethodException e) {

			// 没有找到这2个方法，说明系统版本是2.0之前的旧版本,需要继续寻找下边的 setForeground
			mStartForeground = mStopForeground = null;
			// return;//gxw-
		}

		// 旧版方法，用setForeground
		try {
			mSetForeground = getClass().getMethod("setForeground",
					mSetForegroundSignature);
		} catch (NoSuchMethodException e) {
			throw new IllegalStateException(
					"OS doesn't have Service.startForeground OR Service.setForeground!");
		}

		/* gxw+s for sendPack */

		/* gxw+e */
	}

	// 上传位置$$+L+ID+0x9955+经纬度信息+校验和+\r\n
	public boolean PostLocation(double lat, double lng, float speed,
			float direction, String gpsType, float accurate, double height) {

		Log.i("gps", "gpsType:" + gpsType + "speed:" + speed);
		boolean isPost = true;
		byte[] headBytes = this.m_headBytes;

		// len 移动到最后

		// id 7bytes
		byte[] tuidBytes = this.m_tuidBytes;

		// CMD
		byte[] cmdBytes = new byte[2];
		cmdBytes[0] = (byte) 0x99;
		cmdBytes[1] = 0x55;

		// content x,y
		byte[] contentBytes = null;

		// 转换成度分格式的经纬度
		String strLat = dataConvert.ConverLatForDf(String.valueOf(lat));
		String strLng = dataConvert.ConverLngForDf(String.valueOf(lng));

		// 获得年月日，十分秒
		final Calendar d = Calendar.getInstance(Locale.UK);
		d.setTimeZone(java.util.TimeZone.getTimeZone("GMT+0:00"));
		int Year = d.get(Calendar.YEAR) - 2000; // Get the year
		int Month = d.get(Calendar.MONTH) + 1;// month
		int Day = d.get(Calendar.DAY_OF_MONTH);// day of month
		int Hour = (d.get(Calendar.HOUR_OF_DAY));// hour
		int Minute = d.get(Calendar.MINUTE);// minute
		int Second = d.get(Calendar.SECOND);// second
		int millSecnd = d.get(Calendar.MILLISECOND);

		// 格式化时分秒毫秒 成协议格式
		String strHour = dataConvert.getTwoChars(String.valueOf(Hour));
		String strMin = dataConvert.getTwoChars(String.valueOf(Minute));
		String strSecond = dataConvert.getTwoChars(String.valueOf(Second));
		String strMs = String.valueOf(millSecnd);
		String strHmsMs = strHour + strMin + strSecond + "." + strMs;

		// 格式化年月日 成协议格式
		String strDay = dataConvert.getTwoChars(String.valueOf(Day));
		String strMonth = dataConvert.getTwoChars(String.valueOf(Month));
		String strYear = dataConvert.getTwoChars(String.valueOf(Year));

		String ddmmyy = strDay + strMonth + strYear;

		Log.i("gps", "month:hour:min:senc.ms:" + Month + "," + Hour + ","
				+ Minute + "," + Second + "," + millSecnd);

		StringBuilder JWinfo = new StringBuilder();
		JWinfo.append(strHmsMs + ",").append("A,").append(strLat + ",")
				.append("N,").append(strLng + ",").append("E,")
				.append(speed + ",").append(direction + ",").append(ddmmyy)
				.append("|").append(accurate).append("|").append(height)
				.append("|").append("00000001");

		String content = JWinfo.toString();
		Log.i("gps", "init str:" + content);

		try {
			contentBytes = content.getBytes("UTF-8");
		} catch (UnsupportedEncodingException e1) {
			e1.printStackTrace();
		}

		String sendStr = dataConvert.byteArrToHexStr(contentBytes);
		Log.i("gps", "gps jw 16hexstr:" + sendStr);

		// ccit_sum 放到后边

		// tail
		byte[] tailBytes = this.m_tailBytes;

		// len
		short bytes_len = (short) (headBytes.length + LEN_BYTES
				+ tuidBytes.length + cmdBytes.length + contentBytes.length
				+ SUM_BYTES + tailBytes.length);
		// short转2字节bytes,
		byte[] lenBytes = dataConvert.getBytes(bytes_len, true);

		// 连接各字段组成一个完整的GPS包
		byte[] Head_Len = dataConvert.join(headBytes, lenBytes);
		byte[] Head_Len_Tuid = dataConvert.join(Head_Len, tuidBytes);
		byte[] Head_Len_Tuid_CMD = dataConvert.join(Head_Len_Tuid, cmdBytes);

		// conten
		byte[] Head_Len_Tuid_CMD_Content = dataConvert.join(Head_Len_Tuid_CMD,
				contentBytes);

		// 校验和
		byte[] ccit_sum = null;
		short sum = (short) CRC16.CRC_XModem(Head_Len_Tuid_CMD_Content, 0xffff);
		ccit_sum = dataConvert.getBytes(sum, true);

		// 连接sum
		byte[] Head_Len_Tuid_CMD_Content_Sum = dataConvert.join(
				Head_Len_Tuid_CMD_Content, ccit_sum);

		// 连接tail
		byte[] sendBytes = dataConvert.join(Head_Len_Tuid_CMD_Content_Sum,
				tailBytes);

		try {

			jsock.SendPack(9955, sendBytes);

		} catch (IOException e) {

			isPost = false;

			e.printStackTrace();
		} catch (Exception e) {
			isPost = false;
			e.printStackTrace();
		}

		Toast.makeText(getApplicationContext(),

		"上传位置:" + locData.latitude + "," + locData.longitude, Toast.LENGTH_LONG)
				.show();

		return isPost;

		// byte[] recvBytes = jsock.RecvPack(9955);

	}

	public boolean Login() {
		// head
		byte[] headBytes = this.m_headBytes;

		// len移到最后

		// id 7bytes
		byte[] tuidBytes = this.m_tuidBytes;

		// CMD
		byte[] cmdBytes = new byte[2];
		cmdBytes[0] = 0x50;
		cmdBytes[1] = 0x00;
		// content :无

		// ccit_sum移动到最后

		// tail
		byte[] tailBytes = this.m_tailBytes;

		// len

		short bytes_len = (short) (headBytes.length + LEN_BYTES
				+ tuidBytes.length + cmdBytes.length + 0 + SUM_BYTES + tailBytes.length);

		// short转2字节bytes,
		byte[] len = dataConvert.getBytes(bytes_len, true);

		// 连接各字段组成一个完整的登录包
		byte[] Head_Len = dataConvert.join(headBytes, len);
		byte[] Head_Len_Tuid = dataConvert.join(Head_Len, tuidBytes);
		byte[] Head_Len_Tuid_CMD = dataConvert.join(Head_Len_Tuid, cmdBytes);

		// content在此没有
		byte[] Head_Len_Tuid_CMD_Content = Head_Len_Tuid_CMD;

		// 校验和
		byte[] ccit_sum = null;
		short sum = (short) CRC16.CRC_XModem(Head_Len_Tuid_CMD_Content, 0xffff);
		ccit_sum = dataConvert.getBytes(sum, true);

		// 连接sum
		byte[] Head_Len_Tuid_CMD_Content_Sum = dataConvert.join(
				Head_Len_Tuid_CMD_Content, ccit_sum);

		// 连接tail
		byte[] sendBytes = dataConvert.join(Head_Len_Tuid_CMD_Content_Sum,
				tailBytes);

		String sendStr = dataConvert.byteArrToHexStr(sendBytes);

	

		try {
			jsock.SendPack(5000, sendBytes);
		} catch (IOException e) {

			e.printStackTrace();
		} catch (NullPointerException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		}

		byte[] recvBytes = jsock.RecvPack(5000);

		if (recvBytes != null) {
			if (recvBytes[13] == 1) {
				// login成功
				Log.i("wuliu", "login OK:" + sendStr);
				// Toast.makeText(getApplicationContext(),"Login OK!",Toast.LENGTH_LONG).show();
				return true;
			}
		}

		return false;

	}

	/**
	 * 
	 * @param moodId
	 *            : 图标ID
	 * @param moodId
	 *            : 通知标题
	 * @param textId
	 *            : 通知的详细描述
	 * @param type
	 *            : 通知类型，0为前台服务，1为正常通知
	 * @return
	 */

	private Notification CreateNotification(int moodId, String title,
			int textId, int type) {
		// In this sample, we'll use the same text for the ticker and the
		// expanded notification
		CharSequence text = getText(textId);

		// Set the icon, scrolling text and timestamp.
		// Note that in this example, we pass null for tickerText. We update the
		// icon enough that
		// it is distracting to show the ticker text every time it changes. We
		// strongly suggest
		// that you do this as well. (Think of of the "New hardware found" or
		// "Network connection
		// changed" messages that always pop up)
		Notification notification = new Notification(moodId, null,
				System.currentTimeMillis());

		// The PendingIntent to launch our activity if the user selects this
		// notification
		PendingIntent contentIntent = null;

		if (type == 1) {
			contentIntent = PendingIntent.getActivity(this, 0, new Intent(this,
					MyInfoActivity.class), 0);
		} else {
			contentIntent = PendingIntent.getActivity(this, 0, new Intent(this,
					MapSignInActivity.class), 0);
		}

		// Set the info for the views that show in the notification panel.
		notification.setLatestEventInfo(this, title, text, contentIntent);

		return notification;

	}

	// 更新通知
	private Runnable mTask = new Runnable() {
		public void run() {

			for (int i = 0; i < 1; ++i) {

				if (mCondition.block(1 * 1000))
					break;

				Notification notification = CreateNotification(R.drawable.info,
						getText(R.string.notice).toString(),
						R.string.notice_describ, 1);

				// Send the notification.
				// We use a layout id because it is a unique number. We use it
				// later to cancel.
				mNM.notify(R.string.notice, notification);

			}
			// // Done with our work... stop the service!
			// NotifyingService.this.stopSelf();
		}
	};

	void invokeMethod(Method method, Object[] args) {
		try {
			mStartForeground.invoke(this, mStartForegroundArgs);
		} catch (InvocationTargetException e) {
			// Should not happen.
			Log.w("ApiDemos", "Unable to invoke method", e);
		} catch (IllegalAccessException e) {
			// Should not happen.
			Log.w("ApiDemos", "Unable to invoke method", e);
		}
	}

	/**
	 * This is a wrapper around the new startForeground method, using the older
	 * APIs if it is not available.
	 * 
	 * @param id
	 *            : 通知的唯一ID标识
	 * @notification：配置好的 通知 对象
	 */
	void startForegroundCompat(int id, Notification notification) {
		// If we have the new startForeground API, then use it.
		if (mStartForeground != null) { // 新版API可用

			mStartForegroundArgs[0] = Integer.valueOf(id);
			mStartForegroundArgs[1] = notification; // SERVICE的通知栏

			// 开启
			invokeMethod(mStartForeground, mStartForegroundArgs);

			return;
		}

		// Fall back on the old API.，下边的代码不一定执行
		mSetForegroundArgs[0] = Boolean.TRUE;
		invokeMethod(mSetForeground, mSetForegroundArgs);
		mNM.notify(id, notification);

	}

	/**
	 * This is a wrapper around the new stopForeground method, using the older
	 * APIs if it is not available.
	 */
	void stopForegroundCompat(int id) {
		// If we have the new stopForeground API, then use it.
		if (mStopForeground != null) {
			mStopForegroundArgs[0] = Boolean.TRUE;
			try {
				mStopForeground.invoke(this, mStopForegroundArgs);
			} catch (InvocationTargetException e) {
				// Should not happen.
				Log.w("ApiDemos", "Unable to invoke stopForeground", e);
			} catch (IllegalAccessException e) {
				// Should not happen.
				Log.w("ApiDemos", "Unable to invoke stopForeground", e);
			}
			return;
		}
	}

	/**
	 * 处理intent消息命令
	 */

	void handleCommand(Intent intent) {

		if ("com.xw.locationsvc".equals(intent.getAction())) { // 启动这个服务action
																// name="com.xw.locationsvc"

			Notification notification = CreateNotification(R.drawable.gps,
					getText(R.string.local_service_label).toString(), // title
					R.string.foreground_service_started, 0); // describe

			// 真正启用前台服务
			startForegroundCompat(R.string.local_service_label, notification);

		} else if ("com.xw.stopSvc".equals(intent.getAction())) { // 停止服务命令
			stopForegroundCompat(R.string.foreground_service_started);
		}
	}

	public class LoginRunnable implements Runnable {

		private Context m_context;
		private boolean IsRun =true;

		@Override
		public void run() {

			while (IsRun){      //直到登录成功，每隔30秒尝试一次登录
				
				// 连接服务器
				Socket socket = jsock.SocketConnect(); 
				
				if(socket != null)
				{
				   m_handler.sendEmptyMessage(CONNECT_OK);
				}
				else
				{
					 m_handler.sendEmptyMessage(CONNECT_FAILED);
				}
				
			
                //登录包
				boolean boolLogin = false;

				boolLogin = Login();

				if (boolLogin == true) // 登录成功后，开始定位
				{
					m_handler.sendEmptyMessage(LOGIN_OK);
					break;

				} else {
					
					//login失败,30秒后重新尝试TCP连接和登录	
					m_handler.sendEmptyMessage(LOGIN_FAILED);
					jsock.SocketClose();
					
					try {
						Thread.sleep(30000);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					
				}
			}

		}

	}

	@Override
	public int onStartCommand(Intent intent, int flags, int startId) {

		m_headBytes[0] = '$';
		m_headBytes[1] = '$';
		m_tailBytes[0] = '\r';
		m_tailBytes[1] = '\n';
		m_tuidBytes = dataConvert.getTuidBytes(MainActivity.m_devId);

		CreateHandler();
		this.jsock = new Jsocket();

		// 如果连接成功,启动登录线程
		loginRun = new LoginRunnable();
		loginTrd = new Thread(loginRun);
		loginTrd.start();

		// 将service变为前台service
		handleCommand(intent);

		// 当系统停止掉SERVICE后，重新创建SERVICE继续运行
		// return START_STICKY;
		return START_REDELIVER_INTENT;
		// return super.onStartCommand(intent, flags, startId);
	}

	@Override
	@Deprecated
	public void onStart(Intent intent, int startId) {
		// TODO Auto-generated method stub
		super.onStart(intent, startId);
		handleCommand(intent);
	}

	@SuppressWarnings("deprecation")
	@Override
	public void onDestroy() {
		
		if(mLocClient != null)
		{
			mLocClient.unRegisterLocationListener(myListener);
			mLocClient.stop();
		}
		
		if( (loginTrd != null) && (loginTrd.isAlive()) )
		{
			loginRun.IsRun = false;
		}
		super.onDestroy();
	}

}
